/*
 * Copyright 2011 myTDev.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mytdev.commons.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.zip.ZipException;

/**
 *
 * @author Yann D'Isanto
 */
public final class IOUtil {

    private IOUtil() {
    }

    /**
     * Opens a stream for the specified URL and then copies it into the specified destination file.
     * @param source the URL.
     * @param destination the destination file.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void copy(URL source, File destination) throws IOException {
        copy(source, destination, Proxy.NO_PROXY);
    }

    /**
     * Opens a stream for the specified URL using the specified proxy and then 
     * copies it into the specified destination file.
     * @param source the URL.
     * @param destination the destination file.
     * @param proxy the proxy.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void copy(URL source, File destination, Proxy proxy) throws IOException {
        OutputStream output = new FileOutputStream(destination);
        try {
            copy(source, output, proxy);
        } finally {
            output.close();
        }
    }

    /**
     * Opens a stream for the specified URL and then copies it into the specified destination output stream.
     * @param source the URL.
     * @param output the output stream.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void copy(URL source, OutputStream output) throws IOException {
        copy(source, output, Proxy.NO_PROXY);
    }
    
    /**
     * Opens a stream for the specified URL using the specified proxy and then 
     * copies it into the specified output stream.
     * @param source the URL.
     * @param output the output stream.
     * @param proxy the proxy.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void copy(URL source, OutputStream output, Proxy proxy) throws IOException {
        URLConnection connection = source.openConnection(proxy);
        InputStream input = connection.getInputStream();
        try {
            copy(input, output);
        } finally {
            input.close();
        }
    }

    /**
     * Copies from the specified input stream to the specified output stream.
     * @param input the input stream
     * @param output the output stream
     * @throws IOException if an I/O exception has occurred.
     */
    public static void copy(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[2048];
        int nbRead = 0;
        BufferedInputStream bis = new BufferedInputStream(input, 2048);
        BufferedOutputStream bos = new BufferedOutputStream(output, 2048);
        while ((nbRead = bis.read(buffer)) > 0) {
            bos.write(buffer, 0, nbRead);
            bos.flush();
        }
    }

    /**
     * Zips the specified files from the specified folder into the specofoed 
     * destination file.
     * @param destination the destination file.
     * @param sourceFolder the source folder.
     * @param files the files.
     * @throws ZipException if a ZIP file error has occurred.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void zip(File destination, File sourceFolder, File... files) throws ZipException, IOException {
        zip(destination, Zipper.DEFAULT_COMPRESSION_LEVEL, sourceFolder, files);
    }

    /**
     * Zips the specified files from the specified folder into the specofoed 
     * destination file using the specified compression level.
     * @param destination the destination file.
     * @param compressionLevel the compression level.
     * @param sourceFolder the source folder.
     * @param files the files.
     * @throws ZipException if a ZIP file error has occurred.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void zip(File destination, int compressionLevel, File workingDirectory, File... files) throws ZipException, IOException {
        Zipper zipper = new Zipper(workingDirectory, compressionLevel);
        String[] entries = new String[files.length];
        String wdPath = workingDirectory.getCanonicalPath();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            String entry = file.getCanonicalPath().replaceFirst(Pattern.quote(wdPath), "");
            if (entry.startsWith(File.separator)) {
                entry = entry.substring(1);
            }
            entries[i] = entry;
        }
        zipper.zip(destination, entries);
    }

    /**
     * Unzips the specified file into the specified destination directory.
     * @param file the file to unzip.
     * @param outputDirectory the output directory.
     * @throws ZipException if a ZIP file error has occurred.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void unzip(File file, File outputDirectory) throws ZipException, IOException {
        Zipper zipper = new Zipper(outputDirectory);
        zipper.unzip(file);
    }

    /**
     * Returns properties loaded from the specified file.
     * @param file the file.
     * @return the loaded properties.
     * @throws IOException if an I/O exception has occurred.
     */
    public static Properties loadProperties(File file) throws IOException {
        Properties properties = new Properties();
        loadProperties(file, properties);
        return properties;
    }

    /**
     * Loads properties from the specified file into the specified Properties. 
     * Existing properties are not removed.
     * @param file the file.
     * @return the loaded properties.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void loadProperties(File file, Properties properties) throws IOException {
        FileInputStream fis = new FileInputStream(file);
        try {
            BufferedInputStream bis = new BufferedInputStream(fis);
            properties.load(bis);
        } finally {
            fis.close();
        }
    }

    /**
     * Stores the specified properties into the specified file.
     * @param properties the properties.
     * @param file the file.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void storeProperties(Properties properties, File file) throws IOException {
        storeProperties(properties, file, null);
    }

    /**
     * Stores the specified properties into the specified file with the specified comments.
     * @param properties the properties.
     * @param file the file.
     * @param comments the comments.
     * @throws IOException if an I/O exception has occurred.
     */
    public static void storeProperties(Properties properties, File file, String comments) throws IOException {
        FileOutputStream fos = new FileOutputStream(file);
        try {
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            properties.store(bos, comments);
        } finally {
            fos.close();
        }
    }
}
